#include "prec.h"
#include "threadpool.h"
#include "wx/log.h"

#include <wx/listimpl.cpp>
WX_DEFINE_LIST(wxiTaskList);

IMPLEMENT_DYNAMIC_CLASS(wxiWorkerManager, wxObject)

void* wxiWorkThread::Entry()
{
	wxiTask* task = NULL;
	while (task = m_manager->GetTask())
	{
		task->Execute();
		m_manager->TaskDone(task);
		if (task->ShouldDelete())
			delete task;
	}
	m_manager->WorkerKilled(this);
	return NULL;
}

wxiWorkerManager::wxiWorkerManager( int initcount /*= 5*/, int maxcount /*= 20*/ )
: m_maxCount(maxcount)
, m_mutex()
, m_notEmpty(m_mutex)
, m_allTaskDone(m_mutex)
, m_killing(false)
, m_valid(true)
, m_mutexWorker()
, m_workerKill(m_mutexWorker)
, m_longTaskCount(0)
{
	Init(initcount);
}

wxiWorkerManager::~wxiWorkerManager()
{
	WaitForAllComplete();
	KillAllWorker();
}

void wxiWorkerManager::SetMaxCount( int count )
{
	if (count < (int)m_workers.GetCount())
		return;
	m_maxCount = count;
}

void wxiWorkerManager::Init(int initcnt)
{
	wxMutexLocker locker(m_mutexWorker);
	for (int i = 0; i < initcnt; ++i)
	{
		wxiWorkThread* worker = new wxiWorkThread(this);

		worker->Create();
		worker->Run();

		m_workers.Append(worker);
	}
	wxLogDebug(wxT("Create init thread for thread pool."));
}

void wxiWorkerManager::AppendTask( wxiTask* task )
{
	wxMutexLocker locker(m_mutex);
	
	if (task->IsLongTask()) ++m_longTaskCount;

	m_tasks.push_back(task);
	if (m_tasks.GetCount() - m_longTaskCount > 10)
		IncWorker();
	m_notEmpty.Broadcast();
}

wxiTask* wxiWorkerManager::GetTask( long timeout /*= 0*/ )
{
	wxMutexLocker locker(m_mutex);

	if (timeout > 0)
	{
		while(m_tasks.empty() && m_valid)
			m_notEmpty.WaitTimeout(timeout);
	}
	else
	{
		while (m_tasks.empty() && m_valid)
			m_notEmpty.Wait();
	}

	if (m_tasks.GetCount() > 0)
	{
		wxiTask* task = m_tasks.front();
		m_tasks.pop_front();
		return task;
	}
	else
	{
		return NULL;
	}
}

void wxiWorkerManager::WaitForAllComplete()
{
	wxMutexLocker locker(m_mutex);

	while (m_tasks.GetCount() > 0)
	{
		m_allTaskDone.Wait();
	}
}

int wxiWorkerManager::GetTaskCount()
{
	wxMutexLocker locker(m_mutex);
	return (int)m_tasks.GetCount();
}

void wxiWorkerManager::TaskDone(wxiTask* task)
{
	wxMutexLocker locker(m_mutex);
	if (task->IsLongTask()) --m_longTaskCount;
	if (m_tasks.GetCount() == 0)
	{
		m_allTaskDone.Broadcast();
	}
}

void wxiWorkerManager::KillAllWorker()
{
	wxMutexLocker locker(m_mutexWorker);

	m_valid = false;
	while (m_workers.GetCount() > 0)
	{
		m_killing = true;
		m_notEmpty.Broadcast();
		m_workerKill.Wait();
		m_killing = false;
	}

	wxMilliSleep(20);
}

void wxiWorkerManager::WorkerKilled( wxiWorkThread* worker )
{
	wxMutexLocker locker(m_mutexWorker);

	m_workers.DeleteObject(worker);
	if (m_killing)
		m_workerKill.Broadcast();
}

void wxiWorkerManager::IncWorker()
{
	wxMutexLocker locker(m_mutexWorker);

	for (int i = 0; i < WORKER_INC_SETUP; ++i)
	{
		if (m_workers.GetCount() >= m_maxCount)
			return;
		wxiWorkThread* worker = new wxiWorkThread(this);

		worker->Create();
		worker->Run();

		m_workers.Append(worker);
	}
}

wxString wxiWorkerManager::GetUniqueName() const
{
	return wxT("ThreadPool");
}

